import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from exchange_manager import ExchangeManager
from common_symbols import CommonSymbolsManager
from logs import setup_arbitrage_logger, setup_order_logger
import asyncio
import ccxt.pro as ccxt
from typing import List, Dict
from decimal import Decimal
from database_manager import DatabaseManager
import time
import json
from datetime import datetime
import copy



# 存储所有交易所的价格数据
global_prices: Dict[str, Dict[str, Dict]] = {}

#存储套利机会的价差
global_diff_percents: Dict[str, float] = {}


# 用于线程安全的锁
price_lock = asyncio.Lock()
diff_percents_lock = asyncio.Lock()  # 新增：用于保护global_diff_percents的锁
# 用于时间戳转换的交易所实例
timestamp_exchange = None

# 套利机会保存路径
ARBITRAGE_OPPORTUNITIES_FILE = './arbitrage_opportunities.json'

def save_opportunities(opportunities: List[Dict]):
    """保存套利机会到JSON文件"""
    try:
        # 添加时间戳
        for opp in opportunities:
            opp['save_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        
        # 读取现有数据
        existing_data = []
        if os.path.exists(ARBITRAGE_OPPORTUNITIES_FILE):
            with open(ARBITRAGE_OPPORTUNITIES_FILE, 'r') as f:
                existing_data = json.load(f)
        
        # 合并新数据
        existing_data.extend(opportunities)
        
        # 保存到文件
        with open(ARBITRAGE_OPPORTUNITIES_FILE, 'w') as f:
            json.dump(existing_data, f, indent=4, ensure_ascii=False)
            
    except Exception as e:
        print(f"保存套利机会失败: {e}")

async def _monitor_exchange(exchange_id: str, exchange, symbols: List[str]):
    """监控单个交易所的价格并更新全局数据"""
    global timestamp_exchange

    func_name = 'watchTickers'
    if not exchange.has[func_name]:
        print(f"{exchange_id} 不支持 {func_name}")
        return

    # 保存第一个交易所实例用于时间戳转换
    if timestamp_exchange is None:
        timestamp_exchange = exchange

    while True:
        try:
            tickers = await getattr(exchange, func_name)(symbols, params={})
            async with price_lock:
                for symbol, ticker in tickers.items():
                    if symbol not in global_prices:
                        global_prices[symbol] = {}
                    
                    # 只使用last价格，因为bid和ask可能为None
                    last = ticker.get('last')
                    
                    # 只有当last不为None时才更新价格
                    if last is not None:
                        # 保存最新成交价和时间戳
                        global_prices[symbol][exchange_id] = {
                            'last': last,
                            'timestamp': exchange.milliseconds()
                        }

            # 移除触发价差检查，由单独任务处理
        except Exception as e:
            print(f"交易所 {exchange_id} 错误: {e}")
            if hasattr(exchange, 'unwatchTickers'):
                await exchange.unwatchTickers()
            break

async def price_diff_monitor():
    """独立的价差监控任务，定期检查所有交易所的价格数据"""
    while True:
        # 定期检查价差，避免频繁计算
        await asyncio.sleep(1)  # 每秒检查一次
        
        # 确保有timestamp_exchange可用
        if timestamp_exchange is None:
            continue
            
        await check_spread(timestamp_exchange)

async def check_spread(exchange):
    """检查所有交易对的价差并找出大于阈值的交易机会"""
    
    async with price_lock:
        current_time = exchange.milliseconds()
        threshold = 1.2  # 1.2%价差阈值
        
        for symbol, exchanges in global_prices.items():
            # 收集各交易所的价格数据
            valid_data = []
            for ex_id, data in exchanges.items():
                # 确保数据存在且不为None，并且是最近5秒内的数据
                if current_time - data['timestamp'] < 5000 and data['last'] is not None:
                    valid_data.append({
                        'exchange_id': ex_id,
                        'last': data['last'],
                        'timestamp': data['timestamp']
                    })
            
            if len(valid_data) < 2:
                continue  # 至少需要两个交易所的数据
            
            try:
                # 找到最高价和最低价的交易所
                max_price_entry = max(valid_data, key=lambda x: x['last'])
                min_price_entry = min(valid_data, key=lambda x: x['last'])
                
                max_price = max_price_entry['last']
                min_price = min_price_entry['last']
                max_exchange = max_price_entry['exchange_id']
                min_exchange = min_price_entry['exchange_id']
                
                # 计算价差百分比
                price_diff = (max_price - min_price) / min_price * 100

                # 如果价差大于阈值
                if price_diff >= threshold:
                    # 创建套利机会对象 - 高价交易所卖出，低价交易所买入
                    opportunity = {
                        'symbol': symbol,
                        'sell_exchange': max_exchange,  # 高价交易所卖出
                        'buy_exchange': min_exchange,   # 低价交易所买入
                        'sell_price': max_price,  # 保持原始价格精度
                        'buy_price': min_price,   # 保持原始价格精度
                        'price_diff': price_diff,  # 保存原始价差值，不格式化
                        'timestamp': current_time
                    }

                    # 更新全局价差（使用锁保护）
                    async with diff_percents_lock:
                        global_diff_percents[symbol] = opportunity
                    
                    # 立即排序
                    await sort_global_diff_percents()
                    
            except Exception as e:
                print(f"检查价差时出错 ({symbol}): {e}")

        
async def sort_global_diff_percents():
    """处理套利机会数据并打印排行榜"""
    global global_diff_percents  # 声明使用全局变量

    async with diff_percents_lock:
        # 1. 清理超过30秒的数据
        current_time = timestamp_exchange.milliseconds() if timestamp_exchange else 0
        global_diff_percents = {symbol: opp for symbol, opp in global_diff_percents.items() 
                              if current_time - opp['timestamp'] < 30000}
            
        # 2. 按价差排序（从高到低）
        sorted_items = sorted(
            global_diff_percents.items(),
            key=lambda x: float(x[1]['price_diff']),  # 确保转换为float类型进行比较
            reverse=True
        )
        
        # 3. 限制最多保存50条数据
        if len(sorted_items) > 50:
            sorted_items = sorted_items[:50]
        
        # 4. 更新全局数据
        global_diff_percents = dict(sorted_items)
        
       

async def print_diff_ranking_list():
    """打印排序后的套利机会排行榜"""
    while True:
        if not global_diff_percents:
            await asyncio.sleep(5)  # 没有数据时等待5秒再检查
            continue
            
        # 获取当前时间
        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        
        # 打印排行榜头部
        print("\n=== 套利机会排行榜 ===")
        print(f"更新时间: {current_time}")
        print(f"总机会数: {len(global_diff_percents)}")

        # 定义列宽
        rank_width = 4
        symbol_width = 15
        diff_width = 8
        exchange_width = 8
        price_width = 12
        
        # 构建表头格式
        header = (
            f"{'排名':>{rank_width}} | "
            f"{'交易对':^{symbol_width-4}} | "
            f"{'价差':^{diff_width-2}} | "
            f"{'买入交易所-(价格)':^{exchange_width+6}} | " 
            f"{'卖出交易所-(价格)':^{exchange_width+6}}"
        )
        print("\n" + header)
        print("-" * (rank_width + symbol_width + diff_width + (exchange_width + price_width + 3) * 2 + 10))
        
        # 使用相同的格式打印数据行
        for i, (symbol, opp) in enumerate(global_diff_percents.items(), 1):
            row = (
                f"{i:>{rank_width}} | "
                f"{symbol:<{symbol_width}} | "
                f"{opp['price_diff']:.2f}%{' ' * (diff_width - len(f'{opp['price_diff']:.2f}%'))} | "
                f"{opp['buy_exchange']:>{exchange_width}}:{opp['buy_price']:{price_width}.8f} | "
                f"{opp['sell_exchange']:>{exchange_width}}:{opp['sell_price']:{price_width}.8f}"
            )
            print(row)
        
        print("-" * (rank_width + symbol_width + diff_width + (exchange_width + price_width + 3) * 2 + 10))
        await asyncio.sleep(10)  # 每10秒更新一次
    


async def test():
    exchange_manager = ExchangeManager()
    # 加载市场数据以获取精度
    success = await exchange_manager.load_all_markets()
    if not success:
        print("加载市场数据失败")
        return
    
    db_manager = DatabaseManager()
    common_symbols = db_manager.get_common_symbols()
    print(f"共监控 {len(common_symbols)} 个交易对")

    try:
        tasks = []
        # 启动交易所价格监控任务
        for exchange_id, exchange in exchange_manager.get_all_exchanges().items():
            print(f"启动监控: {exchange_id} - {exchange.name} - 支持watchTickers: {exchange.has['watchTickers']}")
            task = asyncio.create_task(_monitor_exchange(exchange_id, exchange, common_symbols))
            tasks.append(task)
        
        # 添加独立的价差监控任务
        price_diff_task = asyncio.create_task(price_diff_monitor())
        tasks.append(price_diff_task)

        # 添加独立的打印排行榜任务
        print_task = asyncio.create_task(print_diff_ranking_list())
        tasks.append(print_task)
    
        await asyncio.gather(*tasks)

    finally:
        await exchange_manager.close_all_exchanges()
    

asyncio.run(test())